package com.jsc;

import cn.com.xib.sm.utils.SM2Util;
import com.alibaba.fastjson.*;


import java.util.*;


public class XiaMenBankSMUtil {


//    私钥：733aa9f47200597944afbf9393e47bfc0f1600c9650a65fe27326de5e0461b74
//    公钥：0499ecb28e9f2cd2bb6abec6a85f93ad6927355339a1376c0efb9c37ba860c5bc79445030e8349bedc8a13635629d3657d27d0b4ba57705fab61ea8b5877bd2bd0


    /**
     * 行方的公钥和私钥
     */
    private static String privateKey = "58a56fe06e81724a88fc3e2282428e2773af14b9d720032cfa5ebfdadcd9d23e";
    private static String publicKey = "04c7457f9972b25722d1b7144fd27bd470befafaeb1c812db71fc4fc653f5e4d87a76d1069b2661b9feddfbc967a29cb23c223de2d5564159b9d7e33abd4b77eac";

    /**
     * 合作方的公钥和私钥，合作方的私钥生产环境中是不会提供给我方的
     */
    private static String thirdPartyPrivateKey = "041838FF0965F17C9D8C69AA1640523C2AD83ED5AD482A1F59774E75980A6190";
    private static String thirdPartyPublicKey = "0435B0F3DD1BEEE5497EFD806968B8EB78D95EF56B29984CAAFEFC75B4C1014807FF39E94AD86E1202C5779BFFDA57CA5EEAE61689524ECDEC697A7EC7EDDD6E6D";



    /**
     * 生成密钥对
     */

    public void generateKeyPairs() {
        // 1.生成密钥对
        String[] generateKeyPairs = SM2Util.generateKeyPairs();
        System.out.println("私钥：" + generateKeyPairs[0]);
        System.out.println("公钥：" + generateKeyPairs[1]);
    }

    /**
     * 模拟合作方主动发起交易到银行端
     * 生成加密和签名报文流程
     *
     */

    public void partnerSendEncryptAndSignature() {
        // 业务数据字段，根据业务场景需要进行定义
        String plainText = "{\"creditApplicationCode\":\"0120210402772141087\"}";
        getSendPacket(plainText, false);
    }

    /**
     * 模拟银行发起到合作方，合作方处理后，合作方返回结果到银行
     * 生成加密和签名报文流程
     *
     */

    public void partnerBackEncryptAndSignature() {
        // 业务数据字段，根据业务场景需要进行定义
        // 返回 成功时返回 status=1，并根据需要附加业务场景参数
        String plainText = "{\"token\":\"50cfbc40d9abccacac1014eb357b533b8c60e7a41ab5d31ae158d652df139950\",\"status\":\"1\"}";
        // 失败时返回 status=0，并增加ackMsg="流水号不存在，请重新发送" 字段描述错误
//        String plainText = "{\"ackMsg\":\"流水号不存在，请重新发送\",\"status\":\"1\"}";
        getSendPacket(plainText, false);
    }

    /**
     * 模拟银行收到合作方报文后
     * 进行验签和解密
     */
    public void xibVerifyPartnerPacketAndDecrypt() {
        // 接收到的报文
        String msg = "{\"timeStamp\":\"1617706425350\",\"data\":\"040ebd9a674c65b928064e2db1a69142ba7ad96f777fea00e3fd0c8918509714db36fea7e570228be1dd0d06a4c11af4b06fc1b07b13dcd69db791a47aa8b808c6afb7e25c44e816ed5ea7b94b882e6deb19772edca65f3922eca03f65d8a749a3d2694526b1c6c69b6c09637e37ff619a2da86340a0844c3945057181a85dc83139ee493b145144385c8c635f61948a386c59cdfe0e2d2f5a5b2625ae0f77c324e84cfda0670e74f9ef4edd9a9cfbae8a4a96fe21716141e9a9\",\"encrypt\":\"1\",\"sign\":\"MEUCICHN2Gm3yt5PZ4AmX1HynJHC2F4CojhU2vaG5ri3mMtiAiEAqTvbLy306YdTyjyq7szqyaVlZUQTbf5euYlJS9rA75A=\",\"nonceStr\":\"ihccjpmg\"}\n";
        verifyAndDecrypt(msg, false);
    }

    /**
     * 模拟银行端主动发起交易到合作方
     * 生成加密和签名报文流程
     *
     */

    public void xibSendEncryptAndSignature() {
        // 业务数据字段，根据业务场景需要进行定义
        String plainText = "{\"creditApplicationCode\":\"0120210402772141087\"}";
        getSendPacket(plainText, true);
    }

    /**
     * 模拟合作方发起到银行，银行端处理后，银行返回结果到合作方
     * 生成加密和签名报文流程
     *
     */

    public void xibBackEncryptAndSignature() {
        // 业务数据字段，根据业务场景需要进行定义
        // 返回 成功时返回 status=1，并根据需要附加业务场景参数
        String plainText = "{\"token\":\"50cfbc40d9abccacac1014eb357b533b8c60e7a41ab5d31ae158d652df139950\",\"status\":\"1\"}";
        // 失败时返回 status=0，并增加ackMsg="流水号不存在，请重新发送" 字段描述错误
//        String plainText = "{\"ackMsg\":\"流水号不存在，请重新发送\",\"status\":\"1\"}";
        getSendPacket(plainText, true);
    }

    /**
     * 模拟合作方收到报文后
     * 进行验签和解密
     */
    public void partnerVerifyXibPacketAndDecrypt() {
        // 接收到的报文
        String msg = "{\"timeStamp\":\"1617706041385\",\"data\":\"04e506c5a5478c94a338e1b1a14431e5d86734a6d3af776d3e575eb54ee09d92a441b9304f265e6c43654beb2775ed0bb16e813effc58d02999466716d0ad3b13457430ff636c4c1c3d91be5f292bcfff1a72444fcf3da5735c1609310c89389eb08be796c375fe2d03004073ef5e33f9548102baa28a68aa5ee8d9f028683a1da6506edfd0d9589decaa912a813ba99a9fd304caf121d02f6898b843aac953ca1f05a35b73f7d94d9c03ff9413a0100db161ce65493dd0c5730\",\"encrypt\":\"1\",\"sign\":\"MEYCIQDtOXi2JUMU+39mcmFVqd8ZYlZ6jJpbsfaJq1O+jyA+EQIhAK/HtYpcQkwazzQ7rJroVnkdPESsP1edbdtvCyWFYRRp\",\"nonceStr\":\"fpe0a2vr\"}\n";
        verifyAndDecrypt(msg, true);
    }

    public static Map<String,String> verifyAndDecrypt(String msg, boolean msgFromXib) {
        Map<String,String> map=new HashMap<>();
        JSONObject jsonObject = JSON.parseObject(msg);
        String data = jsonObject.getString("data");
        String encrypt = jsonObject.getString("encrypt");
        // 随机数
        String nonceStr = jsonObject.getString("nonceStr");
        // 时间戳
        String timeStamp = jsonObject.getString("timeStamp");
        String signature = jsonObject.getString("sign");
        // 校验报文的时效性
        if (checkHasTimeout(timeStamp, System.currentTimeMillis())) {
            System.err.println("==========该返回报文已超时，请重新提交===生产直接阻断====");
            map.put("code","0");
            map.put("msg","报文已超时");
            return map;
        }
        // 验证签名
        boolean verify = SM2Util.verifySignature(msgFromXib ? publicKey : thirdPartyPublicKey, data+encrypt+nonceStr+timeStamp, signature);
        if(!verify){
            map.put("code","0");
            map.put("msg","验签不通过");
            return map;
        }

        System.out.println("验签结果为: " + verify);
        // 验证签名为true的时候，再进行解密数据
        if ("1".equals(encrypt)) {
            // 使用私钥钥进行解密
            data = SM2Util.decryptBySM2(data, msgFromXib ? thirdPartyPrivateKey : privateKey);
        }
        map.put("code","1");
        map.put("msg","接收成功");
        map.put("data",data);
//        System.out.println(data);
        return map;
    }

    public static Map<String, Object> getSendPacket(String plainText, boolean isXibSend) {
        // 是否加密 1-加密 0-不加密
        String encrypt = "1";
        if ("1".equals(encrypt)) {
            // 使用接收方的公钥进行加密
            plainText = SM2Util.encryptBySM2(plainText, isXibSend ? thirdPartyPublicKey : publicKey);
        }
        // 随机数
        String nonceStr = getRandom();
        // 时间戳
        String timeStamp = "" + System.currentTimeMillis();

        // 使用发起方的私钥对base64的内容进行签名

        String signature = SM2Util.signature(isXibSend ? privateKey : thirdPartyPrivateKey, plainText+encrypt+nonceStr+timeStamp);
        // 生成最终的报文
        Map<String, Object> data = new HashMap<String, Object>();
        data.put("timeStamp", timeStamp); // 时间戳
        data.put("nonceStr", nonceStr); // 随机数
        data.put("data", plainText);  //  报文字段
        data.put("encrypt", encrypt);  //  报文是否加密
        data.put("sign", signature);  // 签名数据
//        System.out.println("请求报文："+JSON.toJSONString(data));
        return data;
    }




    public static Map<String, Object> getSendPacket(String plainText, boolean isXibSend, String timeStamp, String nonceStr) {
        // 是否加密 1-加密 0-不加密
        String encrypt = "1";
        if ("1".equals(encrypt)) {
            // 使用接收方的公钥进行加密
            plainText = SM2Util.encryptBySM2(plainText, isXibSend ? thirdPartyPublicKey : publicKey);
        }
        String signature = SM2Util.signature(isXibSend ? privateKey : thirdPartyPrivateKey, plainText+encrypt+nonceStr+timeStamp);
        // 生成最终的报文
        Map<String, Object> data = new HashMap<String, Object>();
        data.put("timeStamp", timeStamp); // 时间戳
        data.put("nonceStr", nonceStr); // 随机数
        data.put("data", plainText);  //  报文字段
        data.put("encrypt", encrypt);  //  报文是否加密
        data.put("sign", signature);  // 签名数据
        System.out.println("请求报文："+JSON.toJSONString(data));
        return data;
    }

    private static boolean checkHasTimeout(String timeStamp, long reqTime) {
        try {
            int diffTime = (int) ((reqTime-Long.parseLong(timeStamp)) / 1000L);
            /** 报文有效期需在10分钟内，否则视为失效，可防止过期报文重复发送 **/
            return Math.abs(diffTime) > 600;
        } catch (NumberFormatException e) {
            System.out.println("错误：" + e.getMessage());
            return true;
        }
    }

    private static String getRandom() {
        String sources = "0123456789abcdefghijklmnopqrstuvwxyz";
        Random rand = new Random();
        StringBuffer flag = new StringBuffer();

        for (int j = 0; j < 8; ++j) {
            flag.append(sources.charAt(rand.nextInt(35)) + "");
        }

        return String.valueOf(flag);
    }


    public static Map<String,String> verifyAndDecrypt(String msg, boolean msgFromXib,String timeStamp,String nonceStr) {
        System.out.println("解密---------------------------------------");
        Map<String,String> map=new HashMap<>();
        JSONObject jsonObject = JSON.parseObject(msg);
        String data = jsonObject.getString("data");
        String encrypt = jsonObject.getString("encrypt");
        // 随机数
//        String nonceStr = jsonObject.getString("nonceStr");
//        // 时间戳
//        String timeStamp = jsonObject.getString("timeStamp");
        String signature = jsonObject.getString("sign");
        // 校验报文的时效性
        if (checkHasTimeout(timeStamp, System.currentTimeMillis())) {
            System.err.println("==========该返回报文已超时，请重新提交===生产直接阻断====");
            map.put("code","0");
            map.put("msg","报文超时");
            return map;
        }
        // 验证签名
        boolean verify = SM2Util.verifySignature(msgFromXib ? publicKey : thirdPartyPublicKey, data+encrypt+nonceStr+timeStamp, signature);
        if(!verify){
            map.put("code","0");
            map.put("msg","验签不通过");
            return map;
        }

        System.out.println("验签结果为: " + verify);
        // 验证签名为true的时候，再进行解密数据
        if ("1".equals(encrypt)) {
            // 使用私钥钥进行解密
            data = SM2Util.decryptBySM2(data, msgFromXib ? thirdPartyPrivateKey : privateKey);
        }
        map.put("code","1");
        map.put("msg","接收成功");
        map.put("data",data);
//        System.out.println(data);
        return map;
    }

}